package com.dc.java8._3_stream;

import com.dc.java8._1_lambda.Person;
import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Stream 的操作步骤
 * 		1. 创建 Stream
 * 		2. 中间操作
 * 	   *3. 终止操作
 *
 *
 * 		allMatch——检查是否匹配所有元素
 * 		anyMatch——检查是否至少匹配一个元素
 * 		noneMatch——检查是否没有匹配的元素
 * 		findFirst——返回第一个元素
 * 		findAny——返回当前流中的任意元素
 * 		count——返回流中元素的总个数
 * 		max——返回流中最大值
 * 		min——返回流中最小值
 */
public class StreamTest3 {

	List<Person> persons = Arrays.asList(
		new Person("Elsdon", "Jaycob", "Java programmer", "male", 43, 2000.0,Person.Status.BUSY),
		new Person("Tamsen", "Brittany", "Java programmer", "female", 23, 1500.0,Person.Status.FREE),
		new Person("Floyd", "Donny", "Java programmer", "male", 36, 1800.0,Person.Status.BUSY),
		new Person("Sindy", "Jonie", "Java programmer", "female", 32, 1600.0,Person.Status.FREE),
		new Person("Vere", "Hervey", "Java programmer", "male", 23, 1200.0,Person.Status.FREE),
		new Person("Maude", "Jaimie", "Java programmer", "female", 27, 1900.0,Person.Status.BUSY),
		new Person("Shawn", "Randall", "Java programmer", "male", 50, 2300.0,Person.Status.BUSY),
		new Person("Jayden", "Corrina", "Java programmer", "female", 35, 1700.0,Person.Status.VOCATION),
		new Person("Palmer", "Dene", "Java programmer", "male", 33, 2000.0,Person.Status.FREE),
		new Person("Palmer", "Dene", "Java programmer", "male", 33, 2010.0, Person.Status.BUSY),
		new Person("Addison", "Pam", "Java programmer", "female", 34, 1300.0, Person.Status.VOCATION)
		);

	/**
	 * allMatch		检查是否匹配所有元素
	 * anyMatch		检查是否至少匹配一个元素
	 * noneMatch	检查是否没有匹配的元素
	 */
	@Test
	public void test1() {
		// 是否全部匹配
		boolean b1 = persons.stream()
				.allMatch(p -> p.getStatus().equals(Person.Status.BUSY));
		System.out.println(b1);
		
		// 至少有一个匹配
		boolean b2 = persons.stream()
				.anyMatch(p -> p.getStatus().equals(Person.Status.BUSY));
		System.out.println(b2);
		
		// 是否全部都不匹配
		boolean b3 = persons.stream()
				.noneMatch(p -> p.getStatus().equals(Person.Status.FREE));
		System.out.println(b3);
	}

	/**
	 * findFirst	返回第一个元素
	 * findAny		返回当前流中的任意元素
	 */
	@Test
	public void test2() {
		// findFirst	返回第一个元素
		Optional<Person> op1 = persons.stream()
				.sorted(Comparator.comparing(Person::getAge))
				.findFirst();
		System.out.println(op1.get());
		
		System.out.println("--------------------------------");

		// findAny		返回当前流中的任意元素
		Optional<Person> op2 = persons.parallelStream()
				.filter(p -> p.getStatus().equals(Person.Status.FREE))
				.findAny();
		op2.orElse(new Person());
		System.out.println(op2.get());
	}

	/**
	 * count	返回流中元素的总个数
	 * min		返回流中最小值
	 * max		返回流中最大值
	 */
	@Test
	public void test3() {
		// count	返回流中元素的总个数
		long count = persons.stream()
				.filter(p -> p.getStatus().equals(Person.Status.FREE))
				.count();
		System.out.println(count);
		System.out.println("-----------------------------");
		
		// min		返回流中最小值
		Optional<Person> per = persons.parallelStream()
				.min(Comparator.comparing(Person::getAge));
		System.out.println(per.get());
		System.out.println("-----------------------------");

		// max		返回流中最大值
		Optional<Person> per2 = persons.stream()
				.max(Comparator.comparingDouble(Person::getAge));
		System.out.println(per2.get());
	}

	/**
	 * 归约		reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——可以将流中元素反复结合起来，得到一个值。
	 */
	@Test
	public void test4() {
		List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
		Integer sum1 = list.stream()
			.reduce(0 ,(x,y) -> x + y);
		System.out.println(sum1);

		Double sum2 = persons.stream()
				.map(Person::getSalary)
				.reduce(Double::sum)
				.get();
		System.out.println(sum2);

		// 获取首名字中 字符 'o' 出现的次数
		Integer sum3 = persons.parallelStream()
				.map(Person::getFirstName)
				.flatMap(StreamTest2::filterCharacter)
				.map((ch) -> {
					if (ch.equals('o')) {
						return 1;
					} else {
						return 0;
					}
				})
				.reduce(Integer::sum)
				.get();
		System.out.println(sum3);
	}

	/**
	 * collect		将流转换为其他形式。
	 * 				接收一个 Collector接口的实现，用于给Stream中元素做汇总的方法
	 */
	@Test
	public void test5() {
		// 将获取的结果转换为List
		List<String> strs = persons.stream()
				.map(Person::getFirstName)
				.collect(Collectors.toList());

		// 将获取的结果转换为Set
		Set<String> set = persons.stream()
				.map(Person::getFirstName)
				.collect(Collectors.toSet());
		
		// 将获取的结果转换为HashSet
		HashSet<String> hashSet = persons.stream()
			.map(Person::getFirstName)
			.collect(Collectors.toCollection(HashSet::new));

		// 将获取的结果转换为 Map(注意，此种写法，会在key重复时报错，需要使用 第二种方式解决)
		Map<String, Person> map1 = persons.stream().collect(Collectors.toMap(Person::getLastName, s -> s));
		Map<String, Person> map2 = persons.stream().collect(Collectors.toMap(Person::getLastName, s -> s, (k1, k2) -> k2));

	}

	/**
	 * collect		数值运算
	 */
	@Test
	public void test6() {
		// 求最大值
		Optional<Double> op1 = persons.stream()
				.map(Person::getSalary)
				.collect(Collectors.maxBy(Double::compareTo));
		System.out.println(op1.get());
		
		// 求最小值
		Optional<Double> op2 = persons.stream()
				.map(Person::getSalary)
				.collect(Collectors.minBy((x,y) -> Double.compare(x, y)));
		System.out.println(op2.get());
		
		// 求和
		Double sum = persons.stream()
				.collect(Collectors.summingDouble(Person::getSalary));
		System.out.println("sum=" + sum);
		
		// 求平均值
		Double avg = persons.parallelStream()
				.collect(Collectors.averagingDouble(Person::getSalary));
		System.out.println("avg="+ avg);
		
		// 求次数
		Long count = persons.parallelStream()
				.map(Person::getSalary)
				.collect(Collectors.counting());
		System.out.println("count=" + count);
		
		// 求所有的运算
		DoubleSummaryStatistics dss = persons.stream()
				.collect(Collectors.summarizingDouble(Person::getSalary ));
		System.out.println("getAverage" + dss.getAverage());
		System.out.println("getCount" + dss.getCount());
		System.out.println("getMax" + dss.getMax());
		System.out.println("getMin" + dss.getMin());
		System.out.println("getSum" + dss.getSum());
	}
	
	/**
	 *  groupingBy()		结果分组
	 */
	@Test
	public void test7() {
		Map<Person.Status, List<Person>> map1 = persons.stream()
				.collect(Collectors.groupingBy(Person::getStatus));

		for (Person.Status key : map1.keySet()) {
			System.out.println(key + "---" + map1.get(key));
		}
	}
	
	/**
	 * 多级分组
	 */
	@Test
	public void test8() {
		Map<Person.Status, Map<Integer, List<Person>>> map = persons.stream()
				.collect(Collectors.groupingBy(Person::getStatus, Collectors.groupingBy(Person::getAge)));
		
		for (Person.Status s : map.keySet()) {
			Map<Integer, List<Person>> newMap = map.get(s);
			for (Integer i : newMap.keySet()) {
				List<Person> list = newMap.get(i);
				System.out.println(s + "--" + i + "--" +  list);
			}
		}
	}
	
	/**
	 * Collectors.partitioningBy()		分区
	 * 				对年龄进行分区，当年龄 > 30 为true,反之为 false
	 * 				注意：可能获取属性时，没有对应的获取属性方法（如：p.getAge()），可先生成 引用 （如：Map<Boolean, List<Person>> map） 即可
	 */
	@Test
	public void test9() {
		Map<Boolean, List<Person>> map = persons.stream()
				.collect(Collectors.partitioningBy(p -> p.getAge() > 30));

		for (Boolean b : map.keySet()) {
			System.out.println(b + "--" + map.get(b));
		}
	}
	
	/**
	 * Collectors.joining() 			直接拼接
	 * Collectors.joining( str ) 		拼接中 添加 str
	 * Collectors.joining(str, prefix, suffix)	开始，中间，结束 都插入字符
	 */
	@Test
	public void test10() {
		String str1 = persons.stream()
				.map(Person::getFirstName)
				.collect(Collectors.joining());
		System.out.println(str1);

		String str2 = persons.stream()
				.map(Person::getFirstName)
				.collect(Collectors.joining("<->"));
		System.out.println(str2);

		String str3 = persons.stream()
				.map(Person::getFirstName)
				.collect(Collectors.joining(",", "---", "---"));
		System.out.println(str3);	
	}
	
	/**
	 *  Collectors.reducing() 			求和
	 */
	@Test
	public void test11() {
		Optional<Double> sum = persons.stream()
				.map(Person::getSalary)
				.collect(Collectors.reducing(Double::sum));
		System.out.println(sum.get());
	}

}
